#include <stdio.h>
#include <string.h>

#include <zlib.h>


#define     BUFSIZE            5000
#define     TESTSTR            "hello zlib!"
#define     TOGZIPSIZE(n)      n*1.0001+16
#define     MATH162            256
#define     MATH164            65536
#define     MATH166            16777216

// deflateInit() + deflate() + deflateEnd()
// inflateInit() + inflate() + inflateEnd()

int gzip_compress( char* d_dat, long* d_size, char* s_dat, long s_size )
{
    int         ret;
    z_stream    c_stream;

    if ( d_dat == NULL || s_dat == NULL ) {
        return( 1 );
    }
    if ( *d_size < TOGZIPSIZE(s_size) ) {
        return( 1 );
    }

    memset( d_dat, 0, *d_size );

    // 変数初期化
    c_stream.zalloc = (alloc_func)0;
    c_stream.zfree = (free_func)0;
    c_stream.opaque = (voidpf)0;

    // 圧縮初期化
    ret = deflateInit2( &c_stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, MAX_WBITS + 16, 8, Z_DEFAULT_STRATEGY);
    if ( ret != 0 ){
        return( 1 );
    }

    // 圧縮元・圧縮先バッファー設定
    c_stream.next_in  = (Bytef*)s_dat;
    c_stream.next_out = (Bytef*)d_dat;

    // 圧縮
    while( c_stream.total_in < s_size && c_stream.total_out < *d_size ){
        c_stream.avail_in = c_stream.avail_out = 1;
        ret = deflate( &c_stream, Z_NO_FLUSH );
        if ( ret != 0 ){
            return( 1 );
        }
    }

    for ( ; ; ) {
        c_stream.avail_out = 1;
        ret = deflate( &c_stream, Z_FINISH );
        if ( ret == Z_STREAM_END ){
            break;
        } else if ( ret != 0 ) {
            return( 1 );
        }
    }

    // 圧縮終了処理
    ret = deflateEnd(&c_stream);
    if ( ret != 0 ){
        return( 1 );
    }

    *d_size = c_stream.total_out;

    return 0;
}

long gzip_getsize( char* s_dat, long s_size )
{
    long size;

    if ( s_dat == NULL ) {
        return( 0 );
    }

    size = (unsigned char)s_dat[s_size-4];
    size += MATH162*(unsigned char)s_dat[s_size-3];
    size += MATH164*(unsigned char)s_dat[s_size-2];
    size += MATH166*(unsigned char)s_dat[s_size-1];

    return size;
}

// return 0:OK 以外:NG (1:別 2:解凍バッファーサイズが足りない)
int gzip_uncompress( char* d_dat, long* d_size, char* s_dat, long s_size )
{
    int         ret;
    long         gzip_size;
    z_stream    c_stream;

    if ( d_dat == NULL || s_dat == NULL ) {
        return( 1 );
    }

    gzip_size = gzip_getsize( s_dat, s_size );
    printf( "gzip_size: %ld\n", gzip_size );
    if ( gzip_size > *d_size ){
        return( 2 );
    }

    printf( "gzip_size: %ld\n", gzip_size );

    memset( d_dat, 0, *d_size );

    c_stream.zalloc = (alloc_func)0;
    c_stream.zfree = (free_func)0;
    c_stream.opaque = (voidpf)0;

    c_stream.next_in  = s_dat;
    c_stream.avail_in = (uInt)d_size;
    c_stream.next_out = d_dat;

    // 解凍初期化
    ret = inflateInit2( &c_stream, MAX_WBITS + 16 );
    if ( ret != 0 ){
        return( 1 );
    }

    // 解凍
    while( c_stream.total_out < *d_size && c_stream.total_in < s_size ) {
        c_stream.avail_in = c_stream.avail_out = 1;
        ret = inflate(&c_stream, Z_NO_FLUSH);

        if (ret == Z_STREAM_END){
            break;
        } else if ( ret != 0 ){
            return( 1 );
        }
    }

    ret = inflateEnd( &c_stream );
    if ( ret != 0 ){
        return( 1 );
    }

    *d_size = c_stream.total_out;

    return( 0 );
}


int main( int argc, char* argv[] )
{
	int 		ret;
	int 		err;
	int 		t1,t2;
	long 		s_size;
	long 		d_size;
    long        un_size;
	char 		s_str[BUFSIZE];
	char 		d_str[BUFSIZE*2];
    char        un_str[BUFSIZE];
	z_stream 	c_stream;
    FILE        *fp;


    memset( un_str, 0, sizeof( un_str ) );

	// s_str
	strcpy( s_str, TESTSTR );
	s_size = strlen( s_str );

    d_size = sizeof( d_str );
    un_size = sizeof( un_str );

    // ====================================================
    // 圧縮
    ret = gzip_compress( d_str, &d_size, s_str, s_size );

    printf( "ret : %d\n", ret );
    printf( "s_size : %d\n", s_size );
    printf( "d_size : %d\n", d_size );

    // ====================================================
    // 圧縮データをファイルに書き込む
    fp = fopen( "zlib_gzip.log", "wb" );
    fwrite( d_str, 1, d_size, fp );
    fclose( fp );

    // ====================================================
    // 解凍
    ret = gzip_uncompress( un_str, &un_size, d_str, d_size );
    printf( "gzip_uncompress ret: %d\n", ret );
    printf( "d_size : %d\n", un_size );

    printf("gzip_uncompress: %s\n", un_str);

    return 0;
}